#include <linux/module.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/mutex.h>
#include <linux/wait.h>
#include <linux/poll.h>

#define GLOBALMEM_SIZE 0x1000
#define MEM_CLEAR 0x1
#define GLOBALMEM_MAJOR 230

static int globalmem_major = GLOBALMEM_MAJOR;

module_param(globalmem_major, int, S_IRUGO);

// 环形内存区域，最大为 GLOBALMEM_SIZE 大小
// mem_start,mem_end的关系是[mem_start,mem_end)
struct ring_buffer
{
    int mem_start; // 有效数据的起始位置
    int mem_end;   // 有效数据的末尾位置的下一个
    unsigned char mem[GLOBALMEM_SIZE];
};

/**
 * 判断ringbuffer是否为空
 */
int is_ringbuffer_data_empty(struct ring_buffer *buffer)
{
    return buffer->mem_end == buffer->mem_start;
}

void clear_ring_buffer(struct ring_buffer *buffer)
{
    buffer->mem_start = buffer->mem_end;
}

int get_ringbuffer_start(struct ring_buffer *buffer)
{
    return buffer->mem_start;
}

int get_ringbuffer_size(struct ring_buffer *buffer)
{
    return sizeof(buffer->mem);
}

/**
 * 判断环形buffer是否翻转了
 */
int is_ringbuffer_loopback(struct ring_buffer *buffer)
{
    return buffer->mem_end < buffer->mem_start;
}

/**
 * 获取环形buffer数据的大小
 */
int get_ringbuffer_data_size(struct ring_buffer *buffer)
{
    if (buffer->mem_end >= buffer->mem_start)
    {
        return buffer->mem_end - buffer->mem_start;
    }
    return get_ringbuffer_size(buffer) - buffer->mem_start + buffer->mem_end;
}

/**
 * 获取环形buffer可用的内存大小
 */
int get_ringbuffer_avaible_size(struct ring_buffer *buffer)
{
    return get_ringbuffer_size(buffer) - get_ringbuffer_data_size(buffer);
}

/**
 * 从环形buffer从读数据到缓冲区
 * 返回真正读了多少数据
 */
int ringbuffer_read_data(char *buffer, int buffer_size, struct ring_buffer *ring)
{
    int data_len = get_ringbuffer_data_size(ring);
    int try_read_len = data_len > buffer_size ? buffer_size : data_len;
    if (try_read_len == 0)
    {
        return 0;
    }

    if (is_ringbuffer_loopback(ring))
    {
        // [********----------------------------...............*******************]
        // 其中*为数据
        // 这里可能要分多步读

        int tail_data_len = get_ringbuffer_size(ring) - ring->mem_start;
        if (try_read_len <= tail_data_len)
        {
            // 尾部的数据满足要读的数据量
            copy_to_user(buffer, ring->mem + ring->mem_start, try_read_len);
            ring->mem_start += try_read_len;
        }
        else
        {
            // 尾部的数据量不满足要读的数据量，还需要在头部读一部分数据
            // 1,尾部读完
            copy_to_user(buffer, ring->mem + ring->mem_start, tail_data_len);
            // 2，还需要在开头读一部分数据
            copy_to_user(buffer + tail_data_len, ring->mem, try_read_len - tail_data_len);
            ring->mem_start = try_read_len - tail_data_len;
        }
    }
    else
    {
        copy_to_user(buffer, ring->mem + ring->mem_start, try_read_len);
        ring->mem_start += try_read_len;
    }
    return try_read_len;
}

/**
 * 向环形buffer写数据
 * 返回实际写的数据量
 */
int ringbuffer_write_data(char *buffer, int buffer_size, struct ring_buffer *ring)
{
    int avaible_len = get_ringbuffer_avaible_size(ring);
    int try_write_len = buffer_size > avaible_len ? avaible_len : buffer_size;

    if (try_write_len == 0)
    {
        return 0;
    }

    // 判断尾部剩余空间是否足够
    int tail_ava_len = get_ringbuffer_size(ring) - ring->mem_end;
    if (try_write_len > tail_ava_len)
    {
        // 需要回环写
        copy_from_user(ring->mem + ring->mem_end, buffer, tail_ava_len);
        copy_from_user(ring->mem, buffer + tail_ava_len, try_write_len - tail_ava_len);

        ring->mem_end = try_write_len - tail_ava_len;
    }
    else
    {
        // 尾部剩余空间足够
        copy_from_user(ring->mem + ring->mem_end, buffer, try_write_len);
        ring->mem_end += try_write_len;
    }
    return try_write_len;
}

// 支持反复读写
struct globalmem_dev
{
    struct cdev cdev;
    struct mutex mutex; // 互斥信号量
    struct ring_buffer ring_buffer;
    wait_queue_head_t r_queue; // 读等待队列
    wait_queue_head_t w_queue; // 写等待队列
};

struct globalmem_dev *globalmem_devp;

static void globalmem_setup_cdev(struct globalmem_dev *dev, int index);
static int __init globalmem_init(void);
static void __exit globalmem_exit(void);
static ssize_t globalmem_read(struct file *filp, char __user *buf, size_t size, __kernel_loff_t *ppos);
static int globalmem_open(struct inode *inode, struct file *filp);
static __kernel_loff_t globalmem_llseek(struct file *filp, __kernel_loff_t offset, int orig);
static long globalmem_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
static ssize_t globalmem_write(struct file *filp, const char __user *buf, size_t size, __kernel_loff_t *ppos);
static int globalmem_release(struct inode *inode, struct file *filp);

static const struct file_operations globalmem_fops =
    {
        .owner = THIS_MODULE,
        .llseek = globalmem_llseek,
        .read = globalmem_read,
        .write = globalmem_write,
        .unlocked_ioctl = globalmem_ioctl,
        .open = globalmem_open,
};

static void globalmem_setup_cdev(struct globalmem_dev *dev, int index)
{
    int err = 0;
    int devno = MKDEV(globalmem_major, index);

    cdev_init(&dev->cdev, &globalmem_fops);

    dev->cdev.owner = THIS_MODULE;

    err = cdev_add(&dev->cdev, devno, 1);

    if (err)
    {
        printk("globalmem_setup_cdev failed ,err = %d .\n", err);
    }
}

static int __init globalmem_init(void)
{
    int ret = 0;
    dev_t devno = MKDEV(globalmem_major, 0);

    if (globalmem_major)
    {
        ret = register_chrdev_region(devno, 1, "globalmem");
    }
    else
    {
        ret = alloc_chrdev_region(&devno, 0, 1, "globalmem");
    }
    globalmem_major = MAJOR(devno);

    if (ret < 0)
    {
        return ret;
    }
    globalmem_devp = kzalloc(sizeof(struct globalmem_dev), GFP_KERNEL);
    if (!globalmem_devp)
    {
        ret = -ENOMEM;
        goto fail_malloc;
    }

    globalmem_setup_cdev(globalmem_devp, 0);

    globalmem_devp->ring_buffer.mem_start = 0;
    globalmem_devp->ring_buffer.mem_end = 0;
    mutex_init(&globalmem_devp->mutex);
    init_waitqueue_head(&globalmem_devp->r_queue);
    init_waitqueue_head(&globalmem_devp->w_queue);

    return 0;

fail_malloc:
    unregister_chrdev_region(devno, 1);
    return ret;
}

module_init(globalmem_init);

static void __exit globalmem_exit(void)
{
    cdev_del(&globalmem_devp->cdev);
    unregister_chrdev_region(MKDEV(globalmem_major, 0), 1);
    printk("globalmem exit . \n");
}
module_exit(globalmem_exit);

MODULE_LICENSE("GPL");

static ssize_t globalmem_read(struct file *filp, char __user *buf, size_t size, __kernel_loff_t *ppos)
{
    int ret = 0;
    unsigned long p = *ppos; // p为读的索引位置
    struct globalmem_dev *dev = filp->private_data;

    DECLARE_WAITQUEUE(waitCond, current);

    // 这里不能用wait_event，因为判断是否为空，需要加锁。
    mutex_lock(&dev->mutex);
    add_wait_queue(&dev->r_queue, &waitCond);

    while (is_ringbuffer_data_empty(&dev->ring_buffer))
    {
        mutex_unlock(&dev->mutex);

        __set_current_state(TASK_INTERRUPTIBLE);

        printk("global wait for data to read . \n");

        schedule();

        // 可能有信号来，处理信号
        if (signal_pending(current))
        {
            ret = -ERESTARTSYS;
            break;
        }

        mutex_lock(&dev->mutex);
    }

    if (ret == 0)
    {
        // 有数据，可以读
        int nCount = ringbuffer_read_data(buf, size, &dev->ring_buffer);
        *ppos = get_ringbuffer_start(&dev->ring_buffer);

        // 数据读了，就可以继续写了，就可以唤醒在写上等待的进程了
        wake_up_interruptible(&dev->w_queue);

        ret = nCount;
        printk("global read %d bytes data . \n", ret);
    }

    remove_wait_queue(&dev->r_queue, &waitCond);

    mutex_unlock(&dev->mutex);

    return ret;
}

static ssize_t globalmem_write(struct file *filp, const char __user *buf, size_t size, __kernel_loff_t *ppos)
{
    int ret = 0;
    unsigned long p = *ppos; // p为读的索引位置
    struct globalmem_dev *dev = filp->private_data;

    DECLARE_WAITQUEUE(waitCond, current);

    // 这里不能用wait_event，因为判断是否为空，需要加锁。
    mutex_lock(&dev->mutex);
    add_wait_queue(&dev->w_queue, &waitCond);

    while (0 == get_ringbuffer_size(&dev->ring_buffer))
    {
        mutex_unlock(&dev->mutex);

        __set_current_state(TASK_INTERRUPTIBLE);

        printk("global wait for space to write . \n");
        schedule();

        // 可能有信号来，处理信号
        if (signal_pending(current))
        {
            ret = -ERESTARTSYS;
            break;
        }

        mutex_lock(&dev->mutex);
    }

    if (ret == 0)
    {
        // 有数据，可以读
        int nCount = ringbuffer_write_data(buf, size, &dev->ring_buffer);
        *ppos = get_ringbuffer_start(&dev->ring_buffer);

        // 数据读了，就可以继续写了，就可以唤醒在写上等待的进程了
        wake_up_interruptible(&dev->r_queue);

        ret = nCount;
        printk("global write %d bytes data . \n", ret);
    }

    remove_wait_queue(&dev->w_queue, &waitCond);

    mutex_unlock(&dev->mutex);

    return ret;
}

static unsigned int globalmem_poll(struct file *filp, poll_table *wait)
{
    struct globalmem_dev *dev = filp->private_data;
    unsigned int mask = 0;

    mutex_lock(&dev->mutex);
    poll_wait(filp, &dev->r_queue, wait);
    poll_wait(filp, &dev->w_queue, wait);

    if (0 != get_ringbuffer_data_size(&dev->ring_buffer))
    {
        mask |= POLLIN | POLLRDNORM;
    }

    if (0 != get_ringbuffer_avaible_size(&dev->ring_buffer))
    {
        mask |= POLLOUT | POLLWRNORM;
    }

    mutex_unlock(&dev->mutex);
    return mask;
}

static __kernel_loff_t globalmem_llseek(struct file *filp, __kernel_loff_t offset, int orig)
{
    // TODO
    // 该函数需要重写
    return -EINVAL;
}

static long globalmem_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    struct globalmem_dev *dev = filp->private_data;
    switch (cmd)
    {
    case MEM_CLEAR:
    {
        clear_ring_buffer(&dev->ring_buffer);
        printk("globalmem is set to zero . \n");
        break;
    }
    default:
        return -EINVAL;
    }
    return 0;
}

static int globalmem_open(struct inode *inode, struct file *filp)
{
    filp->private_data = globalmem_devp;

    return 0;
}

static int globalmem_release(struct inode *inode, struct file *filp)
{
    return 0;
}